#include <cmath>
#include "Vector3.h"

bool Vector3D::IsEqual(float a, float b) const
{
	return a - b <= Math::EPSILON && b - a <= Math::EPSILON;
}

Vector3D::Vector3D(float a, float b, float c) : x(a), y(b), z(c)
{
}

Vector3D::Vector3D(const Vector3D &rhs) : x(rhs.x), y(rhs.y), z(rhs.z)
{
}

Vector3D::~Vector3D()
{
}

void Vector3D::Set(float a, float b, float c)
{
	x = a;
	y = b;
	z = c;
}

void Vector3D::SetZero(void)
{
	x = y = z = 0.0f;
}

bool Vector3D::IsZero(void) const
{
	return IsEqual(x, 0.f) && IsEqual(y, 0.f) && IsEqual(z, 0.f);
}

Vector3D Vector3D::operator+(const Vector3D& rhs) const
{
	return Vector3D(x + rhs.x, y + rhs.y, z + rhs.z);
}

Vector3D& Vector3D::operator+=(const Vector3D& rhs)
{
	x += rhs.x;
	y += rhs.y;
	z += rhs.z;
	return *this;
}

Vector3D Vector3D::operator-(const Vector3D& rhs) const
{
	return Vector3D(x - rhs.x, y - rhs.y, z - rhs.z);
}

Vector3D& Vector3D::operator-=(const Vector3D& rhs)
{
	x -= rhs.x;
	y -= rhs.y;
	z -= rhs.z;
	return *this;
}

Vector3D Vector3D::operator-(void) const
{
	return Vector3D(-x, -y, -z);
}

Vector3D Vector3D::operator*(float scalar) const
{
	return Vector3D(scalar * x, scalar * y, scalar * z);
}

Vector3D& Vector3D::operator*=(float scalar)
{
	x *= scalar;
	y *= scalar;
	z *= scalar;
	return *this;
}

bool Vector3D::operator==(const Vector3D& rhs) const
{
	return IsEqual(x, rhs.x) && IsEqual(y, rhs.y) && IsEqual(z, rhs.z);
}

bool Vector3D::operator!= (const Vector3D& rhs) const
{
	return !IsEqual(x, rhs.x) || !IsEqual(y, rhs.y) || !IsEqual(z, rhs.z);
}

Vector3D& Vector3D::operator=(const Vector3D& rhs)
{
	x = rhs.x;
	y = rhs.y;
	z = rhs.z;
	return *this;
}

float Vector3D::Length(void) const
{
	return sqrt(x * x + y * y + z * z);
}

float Vector3D::LengthSquared(void) const
{
	return x * x + y * y + z * z;
}

float Vector3D::Dot(const Vector3D& rhs) const
{
	return x * rhs.x + y * rhs.y + z * rhs.z;
}

Vector3D Vector3D::Cross(const Vector3D& rhs) const
{
	return Vector3D(y * rhs.z - z * rhs.y, z * rhs.x - x * rhs.z, x * rhs.y - y * rhs.x);
}

Vector3D Vector3D::Normalized(void) const throw(DivideByZero)
{
	float d = Length();
	if (d <= Math::EPSILON && -d <= Math::EPSILON)
		throw DivideByZero();
	return Vector3D(x / d, y / d, z / d);
}

Vector3D& Vector3D::Normalize(void) throw(DivideByZero)
{
	float d = Length();
	if (d <= Math::EPSILON && -d <= Math::EPSILON)
		throw DivideByZero();
	x /= d;
	y /= d;
	z /= d;
	return *this;
}

std::ostream& operator<< (std::ostream& os, Vector3D& rhs)
{
	os << "[ " << rhs.x << ", " << rhs.y << ", " << rhs.z << " ]";
	return os;
}

Vector3D operator*(float scalar, const Vector3D& rhs)
{
	return rhs * scalar;
}
